#pragma once

#include "FLOW_core/container_types.h"
#include "FLOW_core/StrId.h"
#include "FLOW_core/namespace.h"

#include "FLOW_ents/namespace.h"
#include "FLOW_ents/Component.h"

using namespace CORE;

namespace ENTS
{

	/*!
	 * \brief The entities are composed of components of different types.
	 *
	 * The entities are composed of components of different types.
	 */
	class CEntity
	{
	private:
		//========================= TYPE DEFINITIONS ==============================

		//! Class used to order components by priority
		class PriorityLessOrderer
		{
		public:
			bool operator( ) ( CComponent* ap_first, CComponent* ap_second ) const
			{
				return ap_first->GetPriority( ) < ap_second->GetPriority( );
			}
		};

		//! Type of the component container, the key is the hash of the name of the component
		typedef CHashMultiMap< u32_t, CComponent* > CmpntHashMultiMap;

		//! Type of the container used to order components by priority, this is used to update the components in the right order
		typedef CSet< CComponent*, PriorityLessOrderer > CmpntPriorityOrderedSet;
		//=========================== DATA MEMBERS ================================

		//! Array of components that compose the entity. They are organized by class type
		CmpntHashMultiMap									m_hmm_components;

		//! Array of components ordered by priority to make easy the update process
		CmpntPriorityOrderedSet								m_s_components;

		//! Name of the entity
		CStrId												m_entity_id;

		//! Tells if the entity should update its components or not
		bool_t												m_b_enabled;

		//============================== METHODS ==================================

		// Helper private method to clone all the components from another entity
		void CloneComponentsFrom( const CEntity& ar_ent );

	public:
		//============================== METHODS ==================================

		//! Default constructor
		CEntity( );

		//! Constructor giving an entity name
		CEntity( const CStrId& ar_entity_id );

		//! Destructor
		~CEntity( );

		//! Copy constructor
		CEntity( const CEntity& ar_ent );

		//! Assignment operator
		CEntity& operator = ( const CEntity& ar_ent );

		//! Entity's update method
		void Update( void );

		//! Add a component to the entity, it can be specified if the bindings must be checked on addition
		bool_t Add( CComponent* ap_component, bool_t ab_bind_on_addition );

		//! Remove binding
		bool_t Remove( CComponent* ap_component );

		//! Clones an entity
		CEntity* Clone( void );

		//! Deletes all the components that the entity contains
		void DeleteComponents( void );

		//! Swaps two entities contents
		void Swap( CEntity* ap_entity );

		//! Returns the name of the entity
		CStrId GetName( void );

		//! Sets the name of the entity
		void SetName( const char_t* apsz_entity_name );

		//! Enables or disables the entity logic update
		void SetEnabled( bool_t ab_enabled );

		//! Tells if the entity is enabled
		bool_t IsEnabled( void );

		//! Gets a component of the specified type if exists
		template< typename ComponentType >
		ComponentType* GetComponent( void )
		{
			const t_char* psz_cmpnt_name = ComponentType::GetClassRTTI( )->GetClassName( );
			CmpntHashMultiMap::iterator it = m_hmm_components.find( psz_cmpnt_name );
			if( it != m_hmm_components.end( ) )
				return (*it).second->DynamicType< ComponentType >( );
			else 
				return NULL;
		}

		//! Gets a component of the specified type if exists
		CComponent* GetComponent( const char_t* apsz_cmpnt_type_name )
		{
			if( !apsz_cmpnt_type_name )
				return NULL;

			CStrId strid( apsz_cmpnt_type_name );

			CmpntHashMultiMap::iterator it = m_hmm_components.find( strid.Hash( ) );
			if( it != m_hmm_components.end( ) )
				return (*it).second;
			else
				return NULL;
		}

		//! Gets all the components of the specified type if exists
		template< typename ComponentType >
		uint_t GetComponent( CVector< ComponentType* >& ar_v_cmpnts )
		{
			const t_char* psz_cmpnt_name = ComponentType::GetClassRTTI( )->GetClassName( );
			std::pair< CmpntHashMultiMap::iterator, CmpntHashMultiMap::iterator > range = m_hmm_components.equal_range( psz_cmpnt_name );
			uint_t size = ar_v_cmpnts.size( );
			ar_v_cmpnts.insert( ar_v_cmpnts.end( ), range.first, range.second );
			size = ar_v_cmpnts.size( ) - size; 
			return size;
		}
	};

} //namespace ENTS